package com.ruoyi.common.security.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.constant.TokenConstants;
import com.ruoyi.common.core.utils.JwtUtils;
import com.ruoyi.common.security.auth.AuthUtil;
import com.ruoyi.system.api.model.LoginUser;

import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import start.spring.basic.constant.CacheConstants;
import start.spring.basic.dynamic.DynamicRedisDAO;
import start.spring.basic.ry.core.constant.SecurityConstants;
import start.spring.basic.ry.core.context.SecurityContextHolder;
import start.spring.basic.ry.core.utils.StringUtils;
import start.spring.basic.util.common.StringUtil;

/**
 * ****************************************************************************
 * 权限过滤器，根据业务包配置控制是否需要校验权限
 *
 * @author(作者)：xuyongyun	
 * @date(创建日期)：2023年2月4日
 ******************************************************************************
 */
@Slf4j
@Component
@WebFilter(filterName = "AuthFilter", urlPatterns = "/**")
public class AuthFilter implements Filter {
	
	//系统公共白名单
	private final String[] requestPathWhiteList = new String[] {
			"/auth/get/code","/auth/login","/auth/register"
	};
	
	/** 单个服务中不需要拦截地址，多个地址以英文“,”分隔   */
    @Value("${exclude.valid.paths:}")
    private String excludeValidPath;
	
	@Autowired
    private DynamicRedisDAO redis;

	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		
		String url = request.getRequestURL().toString();
		
		//是否白名单路径
		boolean sfWhiteUrl = false;
		
		String[] excludeValidPaths = null;
    	if(!StringUtil.isEmpty(excludeValidPath)) {
    		excludeValidPaths = excludeValidPath.split(",");
    	}
    	
    	String[] allExcludePathPatterns = requestPathWhiteList;
    	if(excludeValidPaths!=null) {
    		allExcludePathPatterns = new String[requestPathWhiteList.length + excludeValidPaths.length];
    		System.arraycopy(requestPathWhiteList, 0, allExcludePathPatterns, 0, requestPathWhiteList.length);
    		System.arraycopy(excludeValidPaths, 0, allExcludePathPatterns, requestPathWhiteList.length, excludeValidPaths.length);
    		
    	}
		
		for(String whitePath:allExcludePathPatterns) {
			if(url.indexOf(whitePath)>=0) {
				sfWhiteUrl = true;
				break;
			}
		}
		
		if(!sfWhiteUrl)  {
			
			String token = getToken(request);
	        if (StringUtils.isEmpty(token)) {
	            unauthorizedResponse(response, "令牌不能为空");
	        }
	        
	        Claims claims = JwtUtils.parseToken(token);
	        if (claims == null) {
	            unauthorizedResponse(response, "令牌已过期或验证不正确！");
	        }
	        
	        String userid = JwtUtils.getUserId(claims);
	        String username = JwtUtils.getUserName(claims);
	        if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
	            unauthorizedResponse(response, "令牌验证失败");
	        }
	        
	        String userkey = JwtUtils.getUserKey(claims);
	        boolean islogin = redis.hasKey(null, getTokenKey(userkey));
	        if (!islogin) {
	            unauthorizedResponse(response, "登录状态已过期");
	        }
	        
	        SecurityContextHolder.setUserId(userid);
	        SecurityContextHolder.setUserName(username);
	        SecurityContextHolder.setUserKey(userkey);
	        
	        LoginUser loginUser = AuthUtil.getLoginUser(token);
	        
	        SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
	        SecurityContextHolder.set(SecurityConstants.DETAILS_USERNICKNAME, loginUser.getSysUser().getNickName());
	        
		}
		chain.doFilter(request, response);
	}
    
    private void unauthorizedResponse(HttpServletResponse response, String msg) {
        response.setStatus(HttpStatus.UNAUTHORIZED);
        try {
			response.getWriter().write(msg);
		} catch (IOException e) {
			log.error("返回未登录信息io异常", e);
		}
    }
    
    /**
     * 获取请求token
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(TokenConstants.AUTHENTICATION);
        // 如果前端设置了令牌前缀，则裁剪掉前缀
        if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
            token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
        }
        return token;
    }
    
    /**
     * 获取缓存key
     */
    private String getTokenKey(String token) {
        return CacheConstants.LOGIN_TOKEN_KEY + token;
    }

	@Override
	public void destroy() {
		Filter.super.destroy();
	}
}
